﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ComponentModel;
using Ciloci.Flee;
using System.Collections;
using AppFx.Reflection;
using AppFx.Interpretation;

namespace AppFx.ComponentModel.Descriptive
{
	/// <summary>
	/// Ein PropertyDescriptor, welcher anstatt mit einem Property mit einem Ausdruck
	/// arbeitet und demtentsprechende Werte zurückliefert.
	/// Wird zur Darstellung berechneter Werte/Spalten verwendet.
	/// </summary>
	public class EvaluatedPropertyDesciptor : PropertyDescriptorEx
	{
		#region Properties

		/// <summary>
		/// Ein Evaluator, welcher den Ausdruck mit Hilfe des Datenkontexts auswerten kann.
		/// </summary>
		public IEvaluator<object> Evaluator { get; protected set; }

		#endregion

		#region Constructors


		/// <summary>
		/// Erstellt eine neue Instant der <see cref="EvaluatedPropertyDesciptor"/>-Klasse.
		/// </summary>
		/// <param name="context">Der Datenkontext, welcher zur Auswertung des Ausdrucks benötigt wird.</param>
		/// <param name="exampleItem">Das Beispielobjekt, das zum Parsen des Ausdrucks benötigt wird. (strenge Typisierung)</param>
		/// <param name="componentType">Typ der Komponente, auf welcher der PropertyDescriptor angewandt wird.</param>
		/// <param name="propertyType">Typ der Eigenschaft.</param>
		/// <param name="attributes">Die Attribute, welche zur Anpassung des Descriptors ausgewertet werden sollen.</param>
		/// <param name="expression">Der auszuwertende Ausdruck.</param>
		/// <param name="name">Der Name der Eigenschaft.</param>
		public EvaluatedPropertyDesciptor(IIndexList context, object exampleItem, Type componentType, Type propertyType, Attribute[] attributes, string expression, string name)
			: base(componentType, propertyType, attributes, name)
		{
			Evaluator = new FleeEvaluator<object>();
			Evaluator.Context = context;
			Evaluator.Parse(expression, exampleItem);
			_IsReadOnly = true;
		}

		#endregion

		#region Methods

		/// <summary>
		/// Ruft beim Überschreiben in einer abgeleiteten Klasse den aktuellen Wert der Eigenschaft einer Komponente ab.
		/// </summary>
		/// <param name="component">Die Komponente mit der Eigenschaft, deren Wert abgerufen werden soll.</param>
		/// <returns>
		/// Der Wert einer Eigenschaft für eine angegebenen Komponente.
		/// </returns>
		public override object GetValue(object component)
		{
			if (component is IObjectProxy)
			{
				IObjectProxy proxy = component as IObjectProxy;
				object proxied = proxy.ProxiedObject;
				return Evaluator.Evaluate(proxied);
			}
			else
			{
				return Evaluator.Evaluate(component);
			}
		}

		/// <summary>
		/// Legt beim Überschreiben in einer abgeleiteten Klasse den Wert der Komponente auf einen anderen Wert fest.
		/// </summary>
		/// <param name="component">Die Komponente mit dem Eigenschaftswert, der festgelegt werden soll.</param>
		/// <param name="value">Der neue Wert.</param>
		public override void SetValue(object component, object value)
		{
			throw new InvalidOperationException("Since this PropertyDescriptor got calculated value - changing it's value is invalid.");
		}

		#endregion
	}
}
